{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "RDKit WARNING: [08:35:52] Enabling RDKit 2019.09.1 jupyter extensions\n"
     ]
    }
   ],
   "source": [
    "from torch_geometric.data import DataLoader\n",
    "import torch.distributions as D\n",
    "import matplotlib.pyplot as plt\n",
    "from rdkit import Chem, DataStructs\n",
    "from rdkit.Chem import AllChem, Draw, Descriptors, rdMolTransforms\n",
    "from rdkit import rdBase\n",
    "from datetime import datetime\n",
    "import glob\n",
    "import os\n",
    "\n",
    "import deepdock\n",
    "from deepdock.utils.distributions import *\n",
    "from deepdock.utils.data import *\n",
    "from deepdock.models import *\n",
    "\n",
    "# set the random seeds for reproducibility\n",
    "np.random.seed(123)\n",
    "torch.cuda.manual_seed_all(123)\n",
    "torch.manual_seed(123)\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Complexes in training set: 15000\n",
      "Complexes in test set: 1367\n",
      "CPU times: user 3.65 s, sys: 9 s, total: 12.7 s\n",
      "Wall time: 12.7 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "db_complex = PDBbind_complex_dataset(data_path=deepdock.__path__[0]+'/../data/dataset_deepdock_pdbbind_v2019_16K.tar', \n",
    "                                     min_target_nodes=50, max_ligand_nodes=None)\n",
    "db_complex = db_complex.shuffle()\n",
    "db_complex_train = db_complex[:15000]\n",
    "db_complex_test = db_complex[15000:]\n",
    "pdbIDs_test = [db_complex_test.data[i][3] for i in db_complex_test.indices()]\n",
    "pdbIDs_train = [db_complex_train.data[i][3] for i in db_complex_train.indices()]\n",
    "print('Complexes in training set:', len(db_complex_train))\n",
    "print('Complexes in test set:', len(db_complex_test))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "device = 'cuda' if torch.cuda.is_available() else 'cpu'\n",
    "\n",
    "ligand_model = LigandNet(28, residual_layers=10, dropout_rate=0.10)\n",
    "target_model = TargetNet(4, residual_layers=10, dropout_rate=0.10)\n",
    "model = DeepDock(ligand_model, target_model, hidden_dim=64, n_gaussians=10, dropout_rate=0.10, dist_threhold=7.).to(device)\n",
    "\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.002)\n",
    "\n",
    "epochs = 150\n",
    "batch_size = 16\n",
    "save_each=25\n",
    "aux_weight = 0.001\n",
    "losses = []\n",
    "loader_train = DataLoader(db_complex_train, batch_size=batch_size, shuffle=True)\n",
    "loader_test = DataLoader(db_complex_test, batch_size=batch_size, shuffle=False)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "# Load Checkpoint\n",
    "l = pd.read_csv('DeepDock_pdbbindv2019_13K_loss.csv')\n",
    "l = l[l.columns[1:]]\n",
    "losses = l.values.tolist()\n",
    "\n",
    "checkpoint = torch.load('DeepDock_pdbbindv2019_13K_epoch_100.chk')\n",
    "model.load_state_dict(checkpoint['model_state_dict']) \n",
    "optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n",
    "torch.set_rng_state(checkpoint['rng_state'])\n",
    "\n",
    "#RUN\n",
    "# jupyter nbconvert --to notebook --ExecutePreprocessor.timeout=-1 --ExecutePreprocessor.kernel_name=\"python3\" --ExecutePreprocessor.allow_errors=True --execute Train_DeepDock.ipynb &"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Start date: 14/11/2020 at 08:37:32\n",
      "Epoch: 001, Total Loss: 1.692, MDN: 1.692, Atom: 0.247, Bond: 0.120\n",
      "Epoch: 002, Total Loss: 1.610, MDN: 1.610, Atom: 0.032, Bond: 0.030\n",
      "Epoch: 003, Total Loss: 1.602, MDN: 1.602, Atom: 0.015, Bond: 0.021\n",
      "Epoch: 004, Total Loss: 1.593, MDN: 1.593, Atom: 0.009, Bond: 0.017\n",
      "Epoch: 005, Total Loss: 1.583, MDN: 1.583, Atom: 0.008, Bond: 0.014\n",
      "Epoch: 006, Total Loss: 1.573, MDN: 1.573, Atom: 0.006, Bond: 0.012\n",
      "Epoch: 007, Total Loss: 1.561, MDN: 1.561, Atom: 0.006, Bond: 0.011\n",
      "Epoch: 008, Total Loss: 1.556, MDN: 1.556, Atom: 0.005, Bond: 0.011\n",
      "Epoch: 009, Total Loss: 1.547, MDN: 1.547, Atom: 0.004, Bond: 0.011\n",
      "Epoch: 010, Total Loss: 1.538, MDN: 1.538, Atom: 0.004, Bond: 0.010\n",
      "Epoch: 011, Total Loss: 1.530, MDN: 1.530, Atom: 0.004, Bond: 0.010\n",
      "Epoch: 012, Total Loss: 1.524, MDN: 1.524, Atom: 0.004, Bond: 0.009\n",
      "Epoch: 013, Total Loss: 1.518, MDN: 1.518, Atom: 0.004, Bond: 0.009\n",
      "Epoch: 014, Total Loss: 1.513, MDN: 1.513, Atom: 0.004, Bond: 0.009\n",
      "Epoch: 015, Total Loss: 1.494, MDN: 1.494, Atom: 0.004, Bond: 0.008\n",
      "Epoch: 016, Total Loss: 1.442, MDN: 1.442, Atom: 0.004, Bond: 0.009\n",
      "Epoch: 017, Total Loss: 1.422, MDN: 1.422, Atom: 0.005, Bond: 0.010\n",
      "Epoch: 018, Total Loss: 1.405, MDN: 1.405, Atom: 0.004, Bond: 0.009\n",
      "Epoch: 019, Total Loss: 1.394, MDN: 1.394, Atom: 0.004, Bond: 0.009\n",
      "Epoch: 020, Total Loss: 1.384, MDN: 1.384, Atom: 0.004, Bond: 0.010\n",
      "Epoch: 021, Total Loss: 1.375, MDN: 1.375, Atom: 0.003, Bond: 0.011\n",
      "Epoch: 022, Total Loss: 1.368, MDN: 1.368, Atom: 0.003, Bond: 0.012\n",
      "Epoch: 023, Total Loss: 1.363, MDN: 1.362, Atom: 0.004, Bond: 0.011\n",
      "Epoch: 024, Total Loss: 1.357, MDN: 1.357, Atom: 0.003, Bond: 0.012\n",
      "Epoch: 025, Total Loss: 1.351, MDN: 1.351, Atom: 0.003, Bond: 0.014\n",
      "Epoch: 026, Total Loss: 1.346, MDN: 1.346, Atom: 0.003, Bond: 0.014\n",
      "Epoch: 027, Total Loss: 1.342, MDN: 1.342, Atom: 0.004, Bond: 0.014\n",
      "Epoch: 028, Total Loss: 1.337, MDN: 1.337, Atom: 0.004, Bond: 0.015\n",
      "Epoch: 029, Total Loss: 1.332, MDN: 1.332, Atom: 0.005, Bond: 0.016\n",
      "Epoch: 030, Total Loss: 1.328, MDN: 1.328, Atom: 0.004, Bond: 0.017\n",
      "Epoch: 031, Total Loss: 1.323, MDN: 1.323, Atom: 0.005, Bond: 0.018\n",
      "Epoch: 032, Total Loss: 1.319, MDN: 1.319, Atom: 0.004, Bond: 0.019\n",
      "Epoch: 033, Total Loss: 1.315, MDN: 1.315, Atom: 0.004, Bond: 0.019\n",
      "Epoch: 034, Total Loss: 1.311, MDN: 1.311, Atom: 0.004, Bond: 0.020\n",
      "Epoch: 035, Total Loss: 1.307, MDN: 1.307, Atom: 0.005, Bond: 0.021\n",
      "Epoch: 036, Total Loss: 1.303, MDN: 1.303, Atom: 0.005, Bond: 0.020\n",
      "Epoch: 037, Total Loss: 1.300, MDN: 1.300, Atom: 0.006, Bond: 0.022\n",
      "Epoch: 038, Total Loss: 1.296, MDN: 1.296, Atom: 0.005, Bond: 0.022\n",
      "Epoch: 039, Total Loss: 1.292, MDN: 1.292, Atom: 0.006, Bond: 0.022\n",
      "Epoch: 040, Total Loss: 1.289, MDN: 1.289, Atom: 0.005, Bond: 0.023\n",
      "Epoch: 041, Total Loss: 1.286, MDN: 1.286, Atom: 0.006, Bond: 0.023\n",
      "Epoch: 042, Total Loss: 1.282, MDN: 1.282, Atom: 0.006, Bond: 0.024\n",
      "Epoch: 043, Total Loss: 1.280, MDN: 1.280, Atom: 0.006, Bond: 0.026\n",
      "Epoch: 044, Total Loss: 1.277, MDN: 1.277, Atom: 0.006, Bond: 0.025\n",
      "Epoch: 045, Total Loss: 1.274, MDN: 1.274, Atom: 0.006, Bond: 0.026\n",
      "Epoch: 046, Total Loss: 1.271, MDN: 1.271, Atom: 0.006, Bond: 0.026\n",
      "Epoch: 047, Total Loss: 1.268, MDN: 1.268, Atom: 0.007, Bond: 0.028\n",
      "Epoch: 048, Total Loss: 1.267, MDN: 1.267, Atom: 0.006, Bond: 0.028\n",
      "Epoch: 049, Total Loss: 1.263, MDN: 1.263, Atom: 0.007, Bond: 0.029\n",
      "Epoch: 050, Total Loss: 1.260, MDN: 1.260, Atom: 0.007, Bond: 0.030\n",
      "Epoch: 051, Total Loss: 1.258, MDN: 1.258, Atom: 0.007, Bond: 0.029\n",
      "Epoch: 052, Total Loss: 1.256, MDN: 1.256, Atom: 0.008, Bond: 0.031\n",
      "Epoch: 053, Total Loss: 1.253, MDN: 1.253, Atom: 0.007, Bond: 0.032\n",
      "Epoch: 054, Total Loss: 1.250, MDN: 1.250, Atom: 0.009, Bond: 0.033\n",
      "Epoch: 055, Total Loss: 1.248, MDN: 1.248, Atom: 0.009, Bond: 0.033\n",
      "Epoch: 056, Total Loss: 1.245, MDN: 1.245, Atom: 0.008, Bond: 0.034\n",
      "Epoch: 057, Total Loss: 1.243, MDN: 1.243, Atom: 0.008, Bond: 0.033\n",
      "Epoch: 058, Total Loss: 1.239, MDN: 1.239, Atom: 0.009, Bond: 0.035\n",
      "Epoch: 059, Total Loss: 1.236, MDN: 1.236, Atom: 0.008, Bond: 0.034\n",
      "Epoch: 060, Total Loss: 1.232, MDN: 1.232, Atom: 0.009, Bond: 0.034\n",
      "Epoch: 061, Total Loss: 1.224, MDN: 1.224, Atom: 0.009, Bond: 0.035\n",
      "Epoch: 062, Total Loss: 1.219, MDN: 1.219, Atom: 0.009, Bond: 0.035\n",
      "Epoch: 063, Total Loss: 1.214, MDN: 1.214, Atom: 0.010, Bond: 0.035\n",
      "Epoch: 064, Total Loss: 1.212, MDN: 1.212, Atom: 0.010, Bond: 0.037\n",
      "Epoch: 065, Total Loss: 1.208, MDN: 1.208, Atom: 0.010, Bond: 0.039\n",
      "Epoch: 066, Total Loss: 1.205, MDN: 1.205, Atom: 0.010, Bond: 0.037\n",
      "Epoch: 067, Total Loss: 1.203, MDN: 1.203, Atom: 0.010, Bond: 0.036\n",
      "Epoch: 068, Total Loss: 1.200, MDN: 1.199, Atom: 0.011, Bond: 0.037\n",
      "Epoch: 069, Total Loss: 1.198, MDN: 1.197, Atom: 0.010, Bond: 0.038\n",
      "Epoch: 070, Total Loss: 1.195, MDN: 1.195, Atom: 0.011, Bond: 0.039\n",
      "Epoch: 071, Total Loss: 1.192, MDN: 1.192, Atom: 0.011, Bond: 0.040\n",
      "Epoch: 072, Total Loss: 1.190, MDN: 1.190, Atom: 0.011, Bond: 0.041\n",
      "Epoch: 073, Total Loss: 1.187, MDN: 1.187, Atom: 0.011, Bond: 0.042\n",
      "Epoch: 074, Total Loss: 1.185, MDN: 1.185, Atom: 0.011, Bond: 0.041\n",
      "Epoch: 075, Total Loss: 1.184, MDN: 1.184, Atom: 0.011, Bond: 0.041\n",
      "Epoch: 076, Total Loss: 1.181, MDN: 1.181, Atom: 0.011, Bond: 0.041\n",
      "Epoch: 077, Total Loss: 1.179, MDN: 1.179, Atom: 0.011, Bond: 0.042\n",
      "Epoch: 078, Total Loss: 1.177, MDN: 1.177, Atom: 0.010, Bond: 0.042\n",
      "Epoch: 079, Total Loss: 1.174, MDN: 1.174, Atom: 0.012, Bond: 0.043\n",
      "Epoch: 080, Total Loss: 1.173, MDN: 1.173, Atom: 0.013, Bond: 0.043\n",
      "Epoch: 081, Total Loss: 1.170, MDN: 1.170, Atom: 0.012, Bond: 0.043\n",
      "Epoch: 082, Total Loss: 1.168, MDN: 1.168, Atom: 0.012, Bond: 0.043\n",
      "Epoch: 083, Total Loss: 1.168, MDN: 1.168, Atom: 0.012, Bond: 0.043\n",
      "Epoch: 084, Total Loss: 1.166, MDN: 1.166, Atom: 0.012, Bond: 0.042\n",
      "Epoch: 085, Total Loss: 1.162, MDN: 1.162, Atom: 0.013, Bond: 0.044\n",
      "Epoch: 086, Total Loss: 1.161, MDN: 1.160, Atom: 0.012, Bond: 0.044\n",
      "Epoch: 087, Total Loss: 1.160, MDN: 1.159, Atom: 0.012, Bond: 0.044\n",
      "Epoch: 088, Total Loss: 1.158, MDN: 1.157, Atom: 0.012, Bond: 0.044\n",
      "Epoch: 089, Total Loss: 1.158, MDN: 1.158, Atom: 0.012, Bond: 0.045\n",
      "Epoch: 090, Total Loss: 1.154, MDN: 1.154, Atom: 0.013, Bond: 0.045\n",
      "Epoch: 091, Total Loss: 1.152, MDN: 1.152, Atom: 0.012, Bond: 0.045\n",
      "Epoch: 092, Total Loss: 1.151, MDN: 1.151, Atom: 0.013, Bond: 0.045\n",
      "Epoch: 093, Total Loss: 1.151, MDN: 1.151, Atom: 0.012, Bond: 0.047\n",
      "Epoch: 094, Total Loss: 1.147, MDN: 1.147, Atom: 0.015, Bond: 0.046\n",
      "Epoch: 095, Total Loss: 1.146, MDN: 1.146, Atom: 0.013, Bond: 0.047\n",
      "Epoch: 096, Total Loss: 1.145, MDN: 1.145, Atom: 0.014, Bond: 0.049\n",
      "Epoch: 097, Total Loss: 1.143, MDN: 1.143, Atom: 0.014, Bond: 0.047\n",
      "Epoch: 098, Total Loss: 1.141, MDN: 1.141, Atom: 0.013, Bond: 0.047\n",
      "Epoch: 099, Total Loss: 1.141, MDN: 1.141, Atom: 0.013, Bond: 0.047\n",
      "Epoch: 100, Total Loss: 1.138, MDN: 1.138, Atom: 0.013, Bond: 0.046\n",
      "Epoch: 101, Total Loss: 1.137, MDN: 1.137, Atom: 0.013, Bond: 0.047\n",
      "Epoch: 102, Total Loss: 1.136, MDN: 1.136, Atom: 0.014, Bond: 0.049\n",
      "Epoch: 103, Total Loss: 1.133, MDN: 1.133, Atom: 0.013, Bond: 0.047\n",
      "Epoch: 104, Total Loss: 1.132, MDN: 1.132, Atom: 0.014, Bond: 0.048\n",
      "Epoch: 105, Total Loss: 1.131, MDN: 1.131, Atom: 0.014, Bond: 0.049\n",
      "Epoch: 106, Total Loss: 1.129, MDN: 1.129, Atom: 0.013, Bond: 0.049\n",
      "Epoch: 107, Total Loss: 1.128, MDN: 1.128, Atom: 0.014, Bond: 0.049\n",
      "Epoch: 108, Total Loss: 1.127, MDN: 1.127, Atom: 0.014, Bond: 0.051\n",
      "Epoch: 109, Total Loss: 1.125, MDN: 1.125, Atom: 0.014, Bond: 0.050\n",
      "Epoch: 110, Total Loss: 1.124, MDN: 1.124, Atom: 0.013, Bond: 0.050\n",
      "Epoch: 111, Total Loss: 1.123, MDN: 1.123, Atom: 0.015, Bond: 0.051\n",
      "Epoch: 112, Total Loss: 1.122, MDN: 1.122, Atom: 0.016, Bond: 0.052\n",
      "Epoch: 113, Total Loss: 1.119, MDN: 1.119, Atom: 0.015, Bond: 0.051\n",
      "Epoch: 114, Total Loss: 1.118, MDN: 1.118, Atom: 0.014, Bond: 0.050\n",
      "Epoch: 115, Total Loss: 1.117, MDN: 1.117, Atom: 0.015, Bond: 0.050\n",
      "Epoch: 116, Total Loss: 1.116, MDN: 1.115, Atom: 0.015, Bond: 0.051\n",
      "Epoch: 117, Total Loss: 1.115, MDN: 1.114, Atom: 0.015, Bond: 0.051\n",
      "Epoch: 118, Total Loss: 1.113, MDN: 1.113, Atom: 0.015, Bond: 0.052\n",
      "Epoch: 119, Total Loss: 1.111, MDN: 1.111, Atom: 0.016, Bond: 0.052\n",
      "Epoch: 120, Total Loss: 1.112, MDN: 1.112, Atom: 0.016, Bond: 0.053\n",
      "Epoch: 121, Total Loss: 1.109, MDN: 1.109, Atom: 0.015, Bond: 0.054\n",
      "Epoch: 122, Total Loss: 1.108, MDN: 1.108, Atom: 0.016, Bond: 0.052\n",
      "Epoch: 123, Total Loss: 1.105, MDN: 1.105, Atom: 0.017, Bond: 0.053\n",
      "Epoch: 124, Total Loss: 1.106, MDN: 1.106, Atom: 0.016, Bond: 0.055\n",
      "Epoch: 125, Total Loss: 1.103, MDN: 1.103, Atom: 0.015, Bond: 0.054\n",
      "Epoch: 126, Total Loss: 1.102, MDN: 1.102, Atom: 0.016, Bond: 0.054\n",
      "Epoch: 127, Total Loss: 1.102, MDN: 1.102, Atom: 0.014, Bond: 0.055\n",
      "Epoch: 128, Total Loss: 1.099, MDN: 1.099, Atom: 0.014, Bond: 0.053\n",
      "Epoch: 129, Total Loss: 1.099, MDN: 1.099, Atom: 0.015, Bond: 0.054\n",
      "Epoch: 130, Total Loss: 1.098, MDN: 1.098, Atom: 0.016, Bond: 0.055\n",
      "Epoch: 131, Total Loss: 1.096, MDN: 1.096, Atom: 0.017, Bond: 0.054\n",
      "Epoch: 132, Total Loss: 1.095, MDN: 1.095, Atom: 0.017, Bond: 0.054\n",
      "Epoch: 133, Total Loss: 1.095, MDN: 1.095, Atom: 0.016, Bond: 0.055\n",
      "Epoch: 134, Total Loss: 1.093, MDN: 1.093, Atom: 0.016, Bond: 0.055\n",
      "Epoch: 135, Total Loss: 1.091, MDN: 1.091, Atom: 0.016, Bond: 0.056\n",
      "Epoch: 136, Total Loss: 1.090, MDN: 1.090, Atom: 0.017, Bond: 0.055\n",
      "Epoch: 137, Total Loss: 1.089, MDN: 1.089, Atom: 0.016, Bond: 0.056\n",
      "Epoch: 138, Total Loss: 1.088, MDN: 1.088, Atom: 0.018, Bond: 0.058\n",
      "Epoch: 139, Total Loss: 1.087, MDN: 1.087, Atom: 0.016, Bond: 0.056\n",
      "Epoch: 140, Total Loss: 1.086, MDN: 1.086, Atom: 0.018, Bond: 0.055\n",
      "Epoch: 141, Total Loss: 1.085, MDN: 1.085, Atom: 0.017, Bond: 0.056\n",
      "Epoch: 142, Total Loss: 1.084, MDN: 1.084, Atom: 0.018, Bond: 0.056\n",
      "Epoch: 143, Total Loss: 1.082, MDN: 1.082, Atom: 0.018, Bond: 0.056\n",
      "Epoch: 144, Total Loss: 1.082, MDN: 1.082, Atom: 0.018, Bond: 0.056\n",
      "Epoch: 145, Total Loss: 1.081, MDN: 1.081, Atom: 0.017, Bond: 0.058\n",
      "Epoch: 146, Total Loss: 1.080, MDN: 1.080, Atom: 0.017, Bond: 0.057\n",
      "Epoch: 147, Total Loss: 1.079, MDN: 1.078, Atom: 0.017, Bond: 0.058\n",
      "Epoch: 148, Total Loss: 1.078, MDN: 1.078, Atom: 0.016, Bond: 0.057\n",
      "Epoch: 149, Total Loss: 1.078, MDN: 1.078, Atom: 0.017, Bond: 0.056\n",
      "Epoch: 150, Total Loss: 1.075, MDN: 1.075, Atom: 0.016, Bond: 0.057\n",
      "CPU times: user 3d 4h 48min 45s, sys: 14h 48min 45s, total: 3d 19h 37min 30s\n",
      "Wall time: 3d 3h 44min 26s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "now = datetime.now()\n",
    "print(now.strftime(\"Start date: %d/%m/%Y at %H:%M:%S\"))\n",
    "\n",
    "def train():\n",
    "    model.train()\n",
    "\n",
    "    total_loss = 0\n",
    "    mdn_loss = 0\n",
    "    atom_loss = 0\n",
    "    bond_loss = 0\n",
    "    for data in loader_train:\n",
    "        optimizer.zero_grad()\n",
    "        ligand, target, activity, pdbid = data\n",
    "        ligand, target, activity = ligand.to(device), target.to(device), activity[0].unsqueeze(1).to(device)\n",
    "        atom_labels = torch.argmax(ligand.x, dim=1, keepdim=False)\n",
    "        bond_labels = torch.argmax(ligand.edge_attr, dim=1, keepdim=False)\n",
    "        \n",
    "        pi, sigma, mu, dist, atom_types, bond_types, batch = model(ligand, target)\n",
    "        \n",
    "        mdn = mdn_loss_fn(pi, sigma, mu, dist)\n",
    "        mdn = mdn[torch.where(dist <= model.dist_threhold)[0]]\n",
    "        mdn = mdn.mean()\n",
    "        atom = F.cross_entropy(atom_types, atom_labels)\n",
    "        bond = F.cross_entropy(bond_types, bond_labels)\n",
    "        loss = mdn + (atom * aux_weight) + (bond * aux_weight)\n",
    "        \n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        total_loss += loss.item() * (ligand.batch.max().item() + 1)\n",
    "        mdn_loss += mdn.item() * (ligand.batch.max().item() + 1)\n",
    "        atom_loss += atom.item() * (ligand.batch.max().item() + 1)\n",
    "        bond_loss += bond.item() * (ligand.batch.max().item() + 1)\n",
    "        \n",
    "        #print('Step, Total Loss: {:.3f}, MDN: {:.3f}'.format(total_loss, mdn_loss))\n",
    "        if np.isinf(mdn_loss) or np.isnan(mdn_loss): break\n",
    "        \n",
    "    return total_loss / len(loader_train.dataset), mdn_loss / len(loader_train.dataset), atom_loss / len(loader_train.dataset), bond_loss / len(loader_train.dataset)\n",
    "\n",
    "\n",
    "@torch.no_grad()\n",
    "def test(dataset):\n",
    "    model.eval()\n",
    "\n",
    "    loader = DataLoader(dataset, batch_size=batch_size, shuffle=False)\n",
    "    \n",
    "    total_loss = 0\n",
    "    mdn_loss = 0\n",
    "    atom_loss = 0\n",
    "    bond_loss = 0\n",
    "    for data in loader:\n",
    "        ligand, target, activity, pdbid = data\n",
    "        ligand, target, activity = ligand.to(device), target.to(device), activity[0].unsqueeze(1).to(device)\n",
    "        atom_labels = torch.argmax(ligand.x, dim=1, keepdim=False)\n",
    "        bond_labels = torch.argmax(ligand.edge_attr, dim=1, keepdim=False)\n",
    "        \n",
    "        pi, sigma, mu, dist, atom_types, bond_types, batch = model(ligand, target)\n",
    "            \n",
    "        mdn = mdn_loss_fn(pi, sigma, mu, dist)\n",
    "        mdn = mdn[torch.where(dist <= model.dist_threhold)[0]]\n",
    "        mdn = mdn.mean()\n",
    "        atom = F.cross_entropy(atom_types, atom_labels)\n",
    "        bond = F.cross_entropy(bond_types, bond_labels)\n",
    "        loss = mdn + (atom * aux_weight) + (bond * aux_weight)\n",
    "        \n",
    "        total_loss += loss.item() * (ligand.batch.max().item() + 1)\n",
    "        mdn_loss += mdn.item() * (ligand.batch.max().item() + 1)\n",
    "        atom_loss += atom.item() * (ligand.batch.max().item() + 1)\n",
    "        bond_loss += bond.item() * (ligand.batch.max().item() + 1)\n",
    "\n",
    "    return total_loss / len(loader.dataset), mdn_loss / len(loader.dataset), atom_loss / len(loader.dataset), bond_loss / len(loader.dataset)\n",
    "\n",
    "prev_test_total_loss = 1000\n",
    "for epoch in range(1, epochs + 1):\n",
    "    total_loss, mdn_loss, atom_loss, bond_loss = train()\n",
    "    if np.isinf(mdn_loss) or np.isnan(mdn_loss): \n",
    "        print('Inf ERROR')\n",
    "        break\n",
    "    test_total_loss, test_mdn_loss, test_atom_loss, test_bond_loss = test(db_complex_test)\n",
    "    losses.append([total_loss, mdn_loss, atom_loss, bond_loss, test_total_loss, test_mdn_loss, test_atom_loss, test_bond_loss])\n",
    "    \n",
    "    if test_mdn_loss <= prev_test_total_loss:\n",
    "        prev_test_total_loss = test_total_loss\n",
    "        torch.save({'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict(), 'rng_state': torch.get_rng_state(), 'total_loss': total_loss,  \n",
    "                    'mdn_loss': mdn_loss, 'atom_loss': atom_loss, 'bond_loss': bond_loss, 'pdbIDs_train':pdbIDs_train, 'pdbIDs_test':pdbIDs_test}, 'DeepDock_pdbbindv2019_13K_minTestLoss.chk')\n",
    "    l = pd.DataFrame(losses, columns= ['total_loss', 'mdn_loss', 'atom_loss', 'bond_loss', 'test_total_loss', 'test_mdn_loss', 'test_atom_loss', 'test_bond_loss'])\n",
    "    l.to_csv('DeepDock_pdbbindv2019_13K_loss.csv')\n",
    "      \n",
    "    print('Epoch: {:03d}, Total Loss: {:.3f}, MDN: {:.3f}, Atom: {:.3f}, Bond: {:.3f}'.format(epoch, total_loss, mdn_loss, atom_loss, bond_loss))\n",
    "    \n",
    "    if epoch % save_each == 0:\n",
    "        torch.save({'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict(), 'rng_state': torch.get_rng_state(), 'total_loss': total_loss,  \n",
    "                    'mdn_loss': mdn_loss, 'atom_loss': atom_loss, 'bond_loss': bond_loss, 'pdbIDs_train':pdbIDs_train, 'pdbIDs_test':pdbIDs_test}, 'DeepDock_pdbbindv2019_13K_epoch_%.3i.chk'%(epoch))\n",
    "        l = pd.DataFrame(losses, columns= ['total_loss', 'mdn_loss', 'atom_loss', 'bond_loss', 'test_total_loss', 'test_mdn_loss', 'test_atom_loss', 'test_bond_loss'])\n",
    "        l.to_csv('DeepDock_pdbbindv2019_13K_loss.csv')\n",
    "    \n",
    "torch.save({'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict(), 'rng_state': torch.get_rng_state(), 'total_loss': total_loss,  \n",
    "            'mdn_loss': mdn_loss, 'atom_loss': atom_loss, 'bond_loss': bond_loss, 'pdbIDs_train':pdbIDs_train, 'pdbIDs_test':pdbIDs_test}, 'DeepDock_pdbbindv2019_13K_epoch_%.3i.chk'%(epoch))\n",
    "l = pd.DataFrame(losses, columns= ['total_loss', 'mdn_loss', 'atom_loss', 'bond_loss', 'test_total_loss', 'test_mdn_loss', 'test_atom_loss', 'test_bond_loss'])\n",
    "l.to_csv('DeepDock_pdbbindv2019_13K_loss.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x7f370332ac18>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "#l = pd.read_csv('AE_ResMeta_Dropout10_DeepDock_pdbbindv2019_14K_loss.csv')\n",
    "l[['total_loss', 'test_total_loss']].plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
